﻿using System;
using System.Collections.Generic;
using System.Text;
using SSG.Tools;

namespace SSG.Model
{
    public partial class Airport
    {
        /// <summary>
        /// Content for a SID / STAR ese file.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public String ToEse(Type type, GeneratorSettings settings = null)
        {
            if (settings == null) settings = GeneratorSettings.DefaultSettings;
            StringBuilder sb = new StringBuilder();
            List<Procedure> tracks = this.GetTracks(type, settings);
            tracks = BaseEntity.UniqueNameList(tracks); // unify, remove duplicates
            foreach (Procedure track in tracks) {
                NameFilter filter = settings.GetNameFilter(track.GetType());
                // Transitions are a special case
                bool commentOutTrack = false;
                if (typeof (Transition) == type && !filter.ModeIgnore) {
                    // currently I only use filter for transitions
                    bool match = this.MatchesFilter(track, filter);
                    if ((filter.ModeExclude && match) || (filter.ModeInclude && !match)) {
                        // do not show this track
                        if (filter.FilteredAsComments)
                            commentOutTrack = true;
                        else
                            continue;
                    }
                }
                string trackLine = track.ToEse(settings);
                if (commentOutTrack) trackLine = Generator.CommentOutEseOrSctLines(trackLine, Generator.SctEseOutputCommentExtendedSpace);
                sb.Append(trackLine);
            }
            if (sb.Length < 1) return "";
            return type.EseSctFileHeader(this.Name, this.AiracInfo) + sb;
        }

        /// <summary>
        /// Content for SCT format
        /// </summary>
        /// <param name="type"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public String ToSct(Type type, GeneratorSettings settings = null)
        {
            if (settings == null) settings = GeneratorSettings.DefaultSettings;
            if (settings == null) throw new InvalidOperationException("No default settings");
            Setting order = settings.GetSctOrder(type);
            if (order == null) throw new InvalidOperationException("Missing order for " + type.FullName);

            StringBuilder sb = new StringBuilder();
            // Sort order affects many things, so we write an info
            if (settings.ExtendedComments) {
                sb.Append(Generator.SctEseOutputCommentExtendedSpace);
                sb.Append("Sort order \"");
                sb.Append(order.Text.ReplaceNonAsciiCharacters());
                sb.Append("\"\n");
            }

            // Single lines and ordered by runways will be generated as per runway 
            // ReSharper disable PossibleUnintendedReferenceComparison
            bool singleLine = (order == Setting.OrderBySidStarTransition);
            bool byRunway = (singleLine || settings.GetSctOrder(type) == Setting.OrderByRunway);
            // ReSharper restore PossibleUnintendedReferenceComparison

            // tracks per type
            List<Procedure> tracks = this.GetTracks(type, settings);

            // build as per runway or over all tracks as per order
            foreach (Runway runway in this.Runways) {
                StringBuilder rwSb = new StringBuilder();
                // If we do not want to sort by name we use all runways 
                // and "break" at the end
                List<Procedure> scrTracks = byRunway ? tracks.FindAll(t => t.Runways.Contains(runway)) : tracks;
                scrTracks = BaseEntity.UniqueNameList(scrTracks); // unify
                if (scrTracks.NullSafeIsEmpty()) continue;
                string startPoint = null;
                bool headerLineWritten = false;
                if (type == typeof (Sid)) {
                    // Some airports do not have opposite runways, example EDDF 18
                    Runway oppRunway = this.GetOppositeRunway(runway);
                    startPoint = (oppRunway == null) ? runway.SctCoordinates : oppRunway.SctCoordinates;
                }
                foreach (Procedure track in scrTracks) {
                    bool commentOutTrack = false;
                    if (typeof (Transition) == type) {
                        // Specific handling for Transitions
                        // Apply the filter
                        NameFilter filter = settings.GetNameFilter(track.GetType());
                        if (typeof (Transition) == type && !filter.ModeIgnore) {
                            // currently I only use filter for transitions
                            bool match = this.MatchesFilter(track, filter);
                            if ((filter.ModeExclude && match) || (filter.ModeInclude && !match)) {
                                // do not show this track
                                if (filter.FilteredAsComments)
                                    commentOutTrack = true;
                                else
                                    continue;
                            }
                        }
                    }
                    int fixesWritten;
                    string sct = track.ToSct(out fixesWritten, startPoint, byRunway ? runway : null, settings);
                    if (fixesWritten < 1) {
                        rwSb.Append(sct);
                        continue;
                    }

                    // write single or multiple header lines
                    if (!headerLineWritten || singleLine) {
                        headerLineWritten = true;
                        string trackSmartName = ""; // non single line default
                        if (singleLine) trackSmartName = settings.TrackNameWithoutSpaces ? track.SmartNameNoSpaces : track.SmartName;

                        // Intro by headerline
                        Dictionary<string, string> replaceValues = new Dictionary<string, string>
                            {
                                {Generator.PlaceholderAirportIcao, this.Name},
                                {Generator.PlaceholderRunway, byRunway ? runway.Name : ""},
                                // Generates preceding ST, TR, TR
                                {Generator.PlaceholderType, singleLine ? type.TypeToSctEseString(2) : type.TypeToSctEseString()},
                                {Generator.PlaceholderTrackName, trackSmartName}
                            };

                        string nameInfo = Generator.ReplacePatternPlaceholders(settings.SctHeaderlineDesignatorPattern, replaceValues);
                        string headerLine = nameInfo;
                        headerLine = headerLine.PadRight(Fix.SctCol1);
                        headerLine += "N000.00.00.000 E000.00.00.000 N000.00.00.000 E000.00.00.000\n";
                        bool writeHeaderline = !(singleLine && commentOutTrack);
                        if (!singleLine) rwSb.Append(Generator.SctEseOutputCommentCr);
                        if (!writeHeaderline) rwSb.Append(Generator.SctEseOutputCommentExtendedSpace);
                        rwSb.Append(headerLine);
                        if (!singleLine) rwSb.Append(Generator.SctEseOutputCommentCr);
                    }

                    // modify if comment is on
                    if (commentOutTrack) sct = Generator.CommentOutEseOrSctLines(sct, Generator.SctEseOutputCommentExtendedSpace);
                    rwSb.Append(sct);
                }

                // Add fixes for track
                sb.Append(rwSb);

                // break if not ordered by runway
                if (!byRunway) break;
            }
            if (sb.Length < 1) return "";
            return type.EseSctFileHeader(this.Name, this.AiracInfo) + sb;
        }

        /// <summary>
        /// Content in KML format
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        public String ToKml(GeneratorSettings settings = null)
        {
            if (settings == null) settings = GeneratorSettings.DefaultSettings;
            if (settings == null) throw new InvalidOperationException("No default settings");

            const int level = 1;
            Type[] types = new[] {typeof (Sid), typeof (Star), typeof (Transition), typeof (Approach)};
            StringBuilder sb = new StringBuilder(Kml.DocumentStyles(1, settings));

            // Airport itself
            StringBuilder aiportKml = new StringBuilder();
            foreach (Runway runway in this.Runways) {
                aiportKml.Append(runway.ToKml(level + 1, settings));
            }
            sb.Append(Kml.KmlWrapInFolder("Runways " + this.Name, null, aiportKml.ToString(), level));

            // tracks per type
            foreach (Type type in types) {
                StringBuilder kmlPerType = new StringBuilder();
                List<Procedure> tracks = this.GetTracks(type, settings);
                foreach (Runway runway in this.Runways) {
                    List<Procedure> rwTracks = tracks.FindAll(t => t.Runways.Contains(runway));
                    foreach (Procedure rwTrack in rwTracks) {
                        String kmlPerRunway = rwTrack.ToKml(runway, level + 1, settings);
                        kmlPerRunway = Kml.KmlWrapInFolder(rwTrack.Name + " " + runway.Name, null, kmlPerRunway, level + 1);
                        kmlPerType.Append(kmlPerRunway);
                    }
                }
                sb.Append(Kml.KmlWrapInFolder(type.GetClassname(), null, kmlPerType.ToString(), level));
            }
            return Kml.WrapInDocument(this.Name, sb.ToString());
        }
    }
}